package com.pan.draw;



import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class MxGraphXML {




    public void getXML(String entityPackagePath,String outputDirPath){
        AtomicInteger height = new AtomicInteger(100);
        AtomicInteger weight = new AtomicInteger(100);
        AtomicInteger id = new AtomicInteger(0);
        Supplier<Integer> getID = () -> id.getAndIncrement();
        Supplier<EntityGraph> getEntityGraph = () -> new EntityGraph(getID.get());
        String myRootPath = System.getProperty("user.dir");
//        entityDirPath = myRootPath+"\\src\\main\\java\\com\\pan\\entity";
        String entityDirPath = myRootPath + "\\src\\main\\java\\" +entityPackagePath.replaceAll("\\.","\\\\");
        System.out.println("entityDirPath:"+entityDirPath);
        File entityDirFile = new File(entityDirPath);
        File[] entityClassFile = entityDirFile.listFiles();
        Function<String,Class> toClass = entityClassName -> {
            try {
                return Class.forName("com.pan.entity."+entityClassName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        };
        Function<File,String> getClassName = (file)-> file.getName().split("\\.")[0];
        Supplier<Stream<String>> getAllClassNameStream = () -> Arrays.stream(entityClassFile).map(getClassName);
        BiFunction<String,EntityGraph,String> getCircleAndLine = (name, entityGraph) ->{
            Integer[] xy = entityGraph.getCircleNextXY();
            Integer circleId = entityGraph.getNextCircleId();
            if (xy[0]>weight.get()){
                weight.set(xy[0]);
            }
            if (xy[1]>height.get()){
                height.set(xy[1]);
            }
            return  "   <mxCell id=\"circle-"+entityGraph.getNum()+"-"+circleId+"\" value=\""+name+"\" style=\"ellipse;whiteSpace=wrap;html=1;\" parent=\"1\" vertex=\"1\">\n" +
                    "       <mxGeometry x=\""+xy[0]+"\" y=\""+xy[1]+"\" width=\"120\" height=\"60\" as=\"geometry\" />\n" +
                    "   </mxCell>\n"+
                    "   <mxCell id=\"line-"+entityGraph.getNum()+"-"+circleId+"\" value=\"\" style=\"endArrow=none;html=1;\" edge=\"1\" parent=\"1\" source=\"rectangle-"+entityGraph.getNum()+"-"+entityGraph.getNum()+"\" target=\"circle-"+entityGraph.getNum()+"-"+circleId+"\">\n" +
                    "      <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n" +
                    "        <mxPoint x=\""+entityGraph.getRectangleXY()[0]+"\" y=\""+entityGraph.getRectangleXY()[1]+"\" as=\"sourcePoint\" />\n" +
                    "        <mxPoint x=\""+xy[0]+"\" y=\""+xy[1]+"\" as=\"targetPoint\" />\n" +
                    "      </mxGeometry>\n" +
                    "   </mxCell>"
                    ;
        };
        BiFunction<String,EntityGraph,String> getRectangle = (name,entityGraph) ->{
            return  "    <mxCell id=\"rectangle-"+entityGraph.getNum()+"-"+entityGraph.getNum()+"\" value=\""+name+"\" style=\"rounded=0;whiteSpace=wrap;html=1;\" parent=\"1\" vertex=\"1\">\n" +
                    "      <mxGeometry x=\""+entityGraph.getRectangleXY()[0]+"\" y=\""+entityGraph.getRectangleXY()[1]+"\" width=\"120\" height=\"60\" as=\"geometry\" />\n" +
                    "    </mxCell>\n";
        };
        Function<Class,String> getClazzName = clazz -> {
            String allName = clazz.getName();
            String[] names = allName.split("\\.");
            String lastName = names[names.length-1];
            return lastName;
        };
        Function<Class,String[]> getClazzFieldNames = clazz -> Arrays.stream(clazz.getDeclaredFields()).map(Field::getName).toArray(String[]::new);
        BiFunction<EntityGraph,Class,String> drawEntity = (entityGraph,clazz) ->{
            return getRectangle.apply(getClazzName.apply(clazz),entityGraph)
            + Arrays.stream(getClazzFieldNames.apply(clazz))
                    .map(fieldName->getCircleAndLine.apply(fieldName,entityGraph))
                    .reduce("",(x,y)-> x + y);
        };
        Function<Stream<String>,String> getAllEntityGraph = (allClassNameStream) -> allClassNameStream.map(toClass).map(clazz -> drawEntity.apply(getEntityGraph.get(),clazz)).reduce("",(x,y)->x+y);
        Consumer<String> writeXML = (entityDraw) -> {
            String xml = "<mxGraphModel dx=\"1008\" dy=\"644\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\""+(weight.get()+100)+"\" pageHeight=\""+(height.get()+100)+"\" math=\"0\" shadow=\"0\" extFonts=\"Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker\">\n" +
                    "  <root>\n" +
                    "    <mxCell id=\"0\" />\n" +
                    "    <mxCell id=\"1\" parent=\"0\" />\n"
                    +
                    entityDraw
                    +
                    "  </root>\n" +
                    "</mxGraphModel>";
            File dirFile = new File(outputDirPath);
            if (!dirFile.exists()){
                dirFile.mkdir();
            }
            String[] parentDirNames = System.getProperty("user.dir").split("\\\\");
            String rootDirName = parentDirNames[parentDirNames.length-1];
            try {
                FileWriter fileWriter = new FileWriter(outputDirPath+"\\"+rootDirName+"-ER.xml");
                fileWriter.write(xml);
                fileWriter.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        };
        writeXML.accept(getAllEntityGraph.apply(getAllClassNameStream.get()));
    }

}
